home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / mxcode / sb01 / sb.c < prev    next >
C/C++ Source or Header  |  1994-07-11  |  19KB  |  628 lines

  1. /*
  2.  * Play and record digitized sound sample on soundblaster DAC/ADC using DMA.
  3.  * This source code is in the public domain.
  4.  * 
  5.  * Modification History
  6.  *
  7.  *  9-Nov-93    David Baggett       Wrote it based on Sound Blaster
  8.  *              <dmb@ai.mit.edu>    Freedom project and Linux code.
  9.  *
  10.  *  24-Jun-94   Gerhard Kordmann    - modified for recording facilities
  11.  *                                  - added keyboard safety-routines to
  12.  *                                    allow stopping of playing/recording
  13.  *                                  - removed click while buffer switch 
  14.  *                                  - added bugfixes by Grzegorz Jablonski
  15.  *                                    and several safety checks
  16.  *                                  - added free dosmem at end of program
  17.  *    08-Jul-94   Gerhard Kordmann    - click also removed in recording 
  18.  *                                    - changes from dosmem... to memcpy
  19.  *              <kordmann@ldv01.Uni-Trier.de>
  20.  *              <grzegorz@kmm-lx.p.lod.edu.pl>
  21.  */
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <stdio.h>
  25. #include <dos.h>
  26. #include <string.h>
  27. #include <pc.h>
  28. #include "sb.h"
  29.  
  30.  
  31. /*  GO32 DPMI structs for accessing DOS memory. */
  32. static _go32_dpmi_seginfo dosmem;   /* DOS (conventional) memory buffer */
  33.  
  34. static _go32_dpmi_seginfo oldirq_rm;    /* original real mode IRQ */
  35. static _go32_dpmi_registers rm_regs;
  36. static _go32_dpmi_seginfo rm_si;    /* real mode interrupt segment info */
  37.  
  38. static _go32_dpmi_seginfo oldirq_pm;    /* original prot-mode IRQ */
  39. static _go32_dpmi_seginfo pm_si;    /* prot-mode interrupt segment info */
  40.  
  41. /*  Card parameters  */
  42. unsigned int    sb_ioaddr;
  43. unsigned int    sb_irq;
  44. unsigned int    sb_dmachan;
  45.  
  46. /* Is a sound currently playing or recorded ? */
  47. volatile int    sb_dma_active = 0;
  48.  
  49. /* Conventional memory buffers for DMA. */
  50. static volatile int sb_bufnum = 0;
  51. static char     *sb_buf[2];
  52. static unsigned int sb_buflen[2];
  53.  
  54. /* Info about current sample */
  55. static unsigned char    *sb_curdata;    /* pointer to next bit of data */
  56. static unsigned long    sb_curlength;   /* total length length left to play */
  57.  
  58.  
  59. /* DMA chunk size, in bytes.
  60.  * This parameter determines how big our DMA buffers are.  We play
  61.  * the sample by piecing together chunks that are this big.  This
  62.  * means that we don't have to copy the entire sample down into
  63.  * conventional memory before playing it.  (A nice side effect of
  64.  * this is that we can play samples that are longer than 64K.)
  65.  *
  66.  * Setting this is tricky.  If it's too small, we'll get lots
  67.  * of interrupts, and slower machines might not be able to keep
  68.  * up.  Furthermore, the smaller this is, the more grainy the
  69.  * sound will come out.
  70.  *
  71.  * On the other hand, if we make it too big there will be a noticeable
  72.  * delay between a call to sb_play and when the sound actually starts
  73.  * playing, which is unacceptable for things like games where sound
  74.  * effects should be "instantaneous".
  75.  *
  76.  */
  77. #define DMA_CHUNK (16000)
  78.  
  79. /*
  80.  * Define replacements for DOS enable and disable.
  81.  * Be careful about inlining these -- GCC has a tendency to move
  82.  * them around even if you declare them volatile.  (This is definitely
  83.  * true before 2.5.2; may be fixed in 2.5.2.)
  84.  */
  85. void disable()
  86. {
  87.     __asm__ __volatile__ ("cli");
  88. }
  89. void enable()
  90. {
  91.     __asm__ __volatile__ ("sti");
  92. }
  93.  
  94. /* Interrupt handler for recording
  95.  *
  96.  * This is called in both protected mode and in real mode -- this means
  97.  * we don't have to switch modes when we service the interrupt.
  98.  */
  99. void sb_intr_rec(_go32_dpmi_registers *reg)
  100. {
  101.     register unsigned n = sb_bufnum;        /* buffer we just recorded */
  102.  
  103.     inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
  104.  
  105.     sb_rec_buffer(1 - n);                   /* Start next buffer recording */
  106.     
  107.     sb_empty_buffer(n);                     /* Save this buffer */
  108.     
  109.     outportb(0x20, 0x20);                   /* Acknowledge the interrupt */
  110.  
  111.     enable();
  112. }
  113.  
  114. /* Save buffer n in sb_curdata and calculate size for next time */
  115. void sb_empty_buffer(register unsigned n)
  116. {
  117.     if(sb_buflen[n] > 0) {
  118.         memcpy(sb_curdata, (char *)(0xE0000000+(unsigned long) sb_buf[n]),  sb_curlength);
  119. /*
  120.  *        the original version, sometimes spot of misterious crashes 
  121.  *        dosmemget((unsigned long) sb_buf[n], sb_buflen[n], sb_curdata);
  122.  */
  123.         sb_curdata += sb_buflen[n];
  124.         sb_curlength -= sb_buflen[n];
  125.  
  126.         /* for determination of buffersize keep in mind, that right now  */
  127.         /*  the other buffer is recording up to DMA_CHUNK bytes. Only if */
  128.         /*  - after saving this other buffer - there is still work to do,*/
  129.         /*  then this buffer can take the rest in the next round         */
  130.         if (sb_curlength > DMA_CHUNK) {
  131.             if (sb_curlength > 2*DMA_CHUNK)
  132.                 sb_buflen[n] = DMA_CHUNK;
  133.             else
  134.                 sb_buflen[n] = sb_curlength - DMA_CHUNK;
  135.         }
  136.         else 
  137.             sb_buflen[n] = 0;
  138.     }
  139. }
  140.  
  141. void sb_rec_buffer(register unsigned n)
  142. {
  143.     int     t;
  144.     unsigned char   im, tm;
  145.     
  146.     if (sb_buflen[n] == 0) {                /* See if we're already done */
  147.         sb_dma_active = 0;
  148.         return;
  149.     }
  150.     disable();
  151.  
  152.     im = inportb(0x21);                     /* Enable interrupts on PIC */
  153.     tm = ~(1 << sb_irq);
  154.     outportb(0x21,im & tm);
  155.  
  156.     outportb(SB_DMA_MASK, 5);               /* Set DMA mode to 'record' */
  157.     outportb(SB_DMA_FF, 0);
  158.     outportb(SB_DMA_MODE, 0x45);
  159.     
  160.     sb_bufnum = n;                          /* Set transfer address */
  161.     t = (int) ((unsigned long) sb_buf[n] >> 16) ;
  162.     outportb(SB_DMAPAGE + 3, t & 0xFF);
  163.     t = (int) ((unsigned long) sb_buf[n] & 0xFFFF);
  164.     outportb(SB_DMA + 2 * sb_dmachan, t & 0xFF);
  165.     outportb(SB_DMA + 2 * sb_dmachan, t >> 8);
  166.                                             /* Set transfer length byte count */
  167.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) & 0xFF);
  168.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) >> 8);
  169.  
  170.     outportb(SB_DMA_MASK, sb_dmachan);      /* Unmask DMA channel */
  171.  
  172.     enable();
  173.  
  174.     sb_writedac(SB_DMA_ADC);            /* command byte for DMA ADC transfer */
  175.  
  176.     sb_writedac((sb_buflen[n]-1) & 0xFF);       /* sb_write length */
  177.     sb_writedac((sb_buflen[n]-1) >> 8);
  178.  
  179.     /* A sound is recorded now. */
  180.     sb_dma_active = 1;                  /* A sound is recorded now. */
  181. }
  182.  
  183. /* Record a sample through the ADC using DMA. */
  184. /* return number of bytes actually recorded */
  185. unsigned long sb_rec(unsigned char *data, unsigned long length)
  186. {
  187.     sb_install_interrupts(sb_intr_rec); /* Install our interrupt handlers */
  188.  
  189.     sb_curdata = data;                      /* Prime the buffers */
  190.     sb_curlength = length;
  191.     if(length > DMA_CHUNK)
  192.         sb_buflen[0] = DMA_CHUNK;
  193.     else
  194.         sb_buflen[0] = length;
  195.  
  196.     if(length <= DMA_CHUNK)
  197.         sb_buflen[1] = 0;
  198.     else
  199.         if(length > 2*DMA_CHUNK)
  200.             sb_buflen[1] = DMA_CHUNK;
  201.         else
  202.             sb_buflen[1] = length-DMA_CHUNK;
  203.     
  204.     sb_rec_buffer(0);               /* Start the first buffer recording.    */
  205.     while (sb_dma_active)
  206.         if(mykbhit()) {             /* kbhit crashed sometimes              */
  207.             int rest;
  208.             rest = sb_read_counter();       /* samples still to record      */
  209.             sb_writedac(SB_HALT_DMA);       /* stop playing                 */
  210.             rest = sb_buflen[sb_bufnum] - rest;/* samples already recorded  */
  211.             length -= (sb_curlength - rest);/* total samples recorded       */
  212.             sb_buflen[sb_bufnum] = rest;    /* save those samples           */
  213.             sb_empty_buffer(sb_bufnum);
  214.             sb_dma_active = 0;              /* and exit the loop            */
  215.             sb_buflen[0] = sb_buflen[1] = sb_curlength = 0; /* clean up     */
  216.         }
  217.     sb_cleanup_ints();                   /* remove interrupts */
  218.     return length;
  219. }
  220.  
  221.  
  222. void sb_intr_play(_go32_dpmi_registers *reg)
  223. {
  224.     register unsigned n = sb_bufnum;        /* buffer we just played */
  225.  
  226.     inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
  227.     
  228.     sb_play_buffer(1 - n);                  /* Start next buffer player */
  229.     
  230.     sb_fill_buffer(n);          /* Fill this buffer for next time around */
  231.     
  232.     outportb(0x20, 0x20);                   /* Acknowledge the interrupt */
  233.  
  234.     enable();
  235. }
  236.  
  237. /* Fill buffer n with the next data. */
  238. void sb_fill_buffer(register unsigned n)
  239. {
  240.     if (sb_curlength > DMA_CHUNK) {
  241.         sb_buflen[n] = DMA_CHUNK;
  242.         memcpy((char *)(0xE0000000+(unsigned long) sb_buf[n]), sb_curdata, DMA_CHUNK);
  243. /*
  244.  *      dosmemput(sb_curdata, DMA_CHUNK, (unsigned long) sb_buf[n]);
  245.  */
  246.         sb_curlength -= DMA_CHUNK;
  247.         sb_curdata += DMA_CHUNK;
  248.     }
  249.     else if (sb_curlength <= 0) {
  250.         sb_buflen[n] = 0;
  251.         sb_curlength = 0;
  252.     }
  253.     else {
  254.         sb_buflen[n] = sb_curlength;
  255. /*
  256.  *        the original version, sometimes spot of misterious crashes 
  257.  *      dosmemput(sb_curdata, sb_curlength, (unsigned long) sb_buf[n]);
  258.  */
  259.         memcpy((char *)(0xE0000000+(unsigned long) sb_buf[n]), sb_curdata, sb_curlength);
  260.         sb_curdata += sb_curlength;
  261.         sb_curlength = 0;
  262.     }
  263. }
  264.  
  265. void sb_play_buffer(register unsigned n)
  266. {
  267.     int     t;
  268.     unsigned char   im, tm;
  269.     if (sb_buflen[n] <= 0) {                /* See if we're already done */
  270.         sb_dma_active = 0;
  271.         return;
  272.     }
  273.     disable();
  274.  
  275.     im = inportb(0x21);                     /* Enable interrupts on PIC */
  276.     tm = ~(1 << sb_irq);
  277.     outportb(0x21,im & tm);
  278.  
  279.     outportb(SB_DMA_MASK, 5);               /* Set DMA mode 'play' */
  280.     outportb(SB_DMA_FF, 0);
  281.     outportb(SB_DMA_MODE, 0x49);
  282.  
  283.     sb_bufnum = n;                          /* Set transfer address */
  284.     t = (int) ((unsigned long) sb_buf[n] >> 16);
  285.     outportb(SB_DMAPAGE + 3, t);
  286.     t = (int) ((unsigned long) sb_buf[n] & 0xFFFF);
  287.     outportb(SB_DMA + 2 * sb_dmachan, t & 0xFF);
  288.     outportb(SB_DMA + 2 * sb_dmachan, t >> 8);
  289.                                         /* Set transfer length byte count */
  290.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) & 0xFF);
  291.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) >> 8);
  292.  
  293.     outportb(SB_DMA_MASK, sb_dmachan);      /* Unmask DMA channel */
  294.  
  295.     enable();
  296.  
  297.     sb_writedac(SB_DMA_8_BIT_DAC); /* command byte for DMA DAC transfer */
  298.  
  299.     sb_writedac((sb_buflen[n]-1) & 0xFF);       /* sb_write length */
  300.     sb_writedac((sb_buflen[n]-1) >> 8);
  301.  
  302.     sb_dma_active = 1;                      /* A sound is playing now. */
  303. }
  304.  
  305. /* Play a sample through the DAC using DMA. */
  306. void sb_play(unsigned char *data, unsigned long length)
  307. {
  308.  
  309.     sb_install_interrupts(sb_intr_play);    /* Install our interrupt handlers */
  310.     
  311.     sb_curdata = data;                      /* Prime the buffers */
  312.     sb_curlength = length;
  313.  
  314.     sb_fill_buffer(0);
  315.     sb_fill_buffer(1);
  316.     
  317.     sb_play_buffer(0);                  /* Start the first buffer playing. */
  318.     while (sb_dma_active) {                 /* keyboard overflow crashes */
  319.         if(mykbhit()) {                     /*  so does kbhit()          */
  320.             if(mygetch() == 27) {
  321.                 sb_writedac(SB_HALT_DMA);
  322.                 sb_dma_active = 0;
  323.                 sb_buflen[0] = sb_buflen[1] = sb_curlength = 0;
  324.             }
  325.             else
  326.                 kbclear();
  327.         }
  328.     }
  329.     sb_cleanup_ints();                           /* remove interrupts */
  330. }
  331.  
  332. /* Set sampling/playback rate.
  333.  * Parameter is rate in Hz (samples per second).
  334.  */
  335. void sb_set_sample_rate(unsigned int rate)
  336. {
  337.     unsigned char tc = (unsigned char) (256 - 1000000/rate);
  338.  
  339.     sb_writedac(SB_TIME_CONSTANT);  /* Command byte for sample rate */
  340.     sb_writedac(tc);        /* Sample rate time constant */
  341. printf("\nRate %d = tc %d\n",rate,tc);
  342. }
  343.  
  344. void sb_voice(int state)
  345. {
  346.     sb_writedac(state ? SB_SPEAKER_ON : SB_SPEAKER_OFF);
  347. }
  348.  
  349. /* Read soundblaster card parameters from BLASTER enivronment variable .*/
  350. void sb_getparams()
  351. {
  352.     char *t, *blaster;
  353.  
  354.     sb_ioaddr = 0x220;          /* Set arguments to Soundblaster defaults */
  355.     sb_irq = 7;
  356.     sb_dmachan = 1;
  357.  
  358.     t = getenv("BLASTER");
  359.     if (!t)
  360.         return;
  361.  
  362.     blaster = strdup(t);                /* Get a copy */
  363.  
  364.     t = strtok(blaster, " \t");         /* Parse the BLASTER variable */
  365.     while (t) {
  366.         switch (t[0]) {
  367.             case 'A':
  368.             case 'a':
  369.                 /* I/O address */
  370.                 sscanf(t + 1, "%x", &sb_ioaddr);
  371.                 break;
  372.             case 'I':
  373.             case 'i':
  374.                 /* IRQ */
  375.                 sb_irq = atoi(t + 1);
  376.                 break;
  377.             case 'D':
  378.             case 'd':
  379.                 /* DMA channel */
  380.                 sb_dmachan = atoi(t + 1);
  381.                 break;
  382.             case 'T':
  383.             case 't':
  384.                 /* what is this? */
  385.                 break;
  386.                 
  387.             default:
  388.                 printf("Unknown BLASTER option %c\n",t[0]);
  389.                 break;
  390.         }
  391.         t = strtok(NULL," \t");
  392.     }
  393.  
  394.     free(blaster);  
  395.     return;
  396. }
  397.  
  398. /* Init the soundblaster card. */
  399. /* gk : modified for autodetection */
  400. int sb_initcard(void)
  401. {
  402.     int i,j,error=0;
  403.     int NrOfBases = 6;
  404.     int Bases[] = {0x210,0x220,0x230,0x240,0x250,0x260};
  405.     int Base;
  406.  
  407.     for(i=0;i<NrOfBases && error == 0;i++) {
  408.         Base = Bases[i];
  409.         outportb(Base + SB_DSP_RESET,1);
  410.  
  411.         inportb(Base + SB_DSP_RESET);              /* Kill some time */
  412.         inportb(Base + SB_DSP_RESET);
  413.         inportb(Base + SB_DSP_RESET);
  414.         inportb(Base + SB_DSP_RESET);
  415.     
  416.         outportb(Base + SB_DSP_RESET, 0);
  417.         for(j=0;j<100;j++) {
  418.             if(sb_read_dac(Base) == 0xAA) {
  419.                 error = 1;
  420.                 break;
  421.             }
  422.         }
  423.     }
  424.     if(error == 0) 
  425.         Base = 0;
  426.     return(Base);
  427. }
  428.  
  429. int sb_read_dac(int Base)
  430. {
  431.     int i;
  432.  
  433.     for(i=0;i<10000;i++) {
  434.         if(inportb(Base + SB_DSP_DATA_AVAIL) & 0x080)
  435.             break;
  436.     }
  437.     return(inportb(Base+0x0A));
  438. }
  439.  
  440.  
  441. void sb_install_interrupts(void (*sb_intr)(_go32_dpmi_registers *))
  442. {
  443.     sb_install_rm_interrupt(sb_intr);
  444.     sb_install_pm_interrupt(sb_intr);
  445. }
  446.  
  447. /*
  448.  * Install our interrupt as the real mode interrupt handler for 
  449.  * the IRQ the soundblaster is on.
  450.  *
  451.  * We accomplish this by have GO32 allocate a real mode callback for us.
  452.  * The callback packages our protected mode code up in a real mode wrapper.
  453.  */
  454. void sb_install_rm_interrupt(void (*sb_intr)(_go32_dpmi_registers *))
  455. {
  456.     int ret;
  457.  
  458.     rm_si.pm_offset = (int) sb_intr;
  459.     ret = _go32_dpmi_allocate_real_mode_callback_iret(&rm_si, &rm_regs);
  460.     if (ret != 0) {
  461.         printf("cannot allocate real mode callback, error=%04x\n",ret);
  462.         exit(1);
  463.     }
  464.  
  465.     disable();
  466.     _go32_dpmi_get_real_mode_interrupt_vector(8 + sb_irq, &oldirq_rm);
  467.     _go32_dpmi_set_real_mode_interrupt_vector(8 + sb_irq, &rm_si);
  468.     enable();
  469. }
  470.  
  471. /* Remove our real mode interrupt handler. */
  472. void sb_cleanup_rm_interrupt()
  473. {
  474.     disable();
  475.     _go32_dpmi_set_real_mode_interrupt_vector(8 + sb_irq, &oldirq_rm);
  476. /* gk : added safety check */
  477.     if(rm_si.size != -1)
  478.         _go32_dpmi_free_real_mode_callback(&rm_si);
  479.     rm_si.size = -1;
  480.     enable();
  481. }
  482.  
  483. /* Install our interrupt as the protected mode interrupt handler for 
  484.  * the IRQ the soundblaster is on. */
  485. void sb_install_pm_interrupt(void (*sb_intr)(_go32_dpmi_registers *))
  486. {
  487.     int ret;
  488.     disable();
  489.  
  490.     pm_si.pm_offset = (int) sb_intr;
  491. /* changes to wrap by grzegorz */
  492.     ret = _go32_dpmi_allocate_iret_wrapper(&pm_si);
  493.     if (ret != 0) {
  494.         printf("cannot allocate protected mode wrapper, error=%04x\n",ret);
  495.         exit(1);
  496.     }
  497.  
  498.     pm_si.pm_selector = _go32_my_cs();
  499.     _go32_dpmi_get_protected_mode_interrupt_vector(8 + sb_irq, &oldirq_pm);
  500.     _go32_dpmi_set_protected_mode_interrupt_vector(8 + sb_irq, &pm_si);
  501.     enable();
  502. }
  503.  
  504. /* Remove our protected mode interrupt handler. */
  505. void sb_cleanup_pm_interrupt()
  506. {
  507.     disable();
  508. /* changes to wrap by grzegorz, safety chek by gk */
  509.     if(pm_si.size != -1)
  510.         _go32_dpmi_free_iret_wrapper(&pm_si);
  511.     pm_si.size = -1;
  512.  
  513.     _go32_dpmi_set_protected_mode_interrupt_vector(8 + sb_irq, &oldirq_pm);
  514.     enable();
  515. }
  516.  
  517. /* Allocate conventional memory for our DMA buffers.
  518.  * Each DMA buffer must be aligned on a 64K boundary in physical memory. */
  519. int sb_init_buffers()
  520. {
  521.     dosmem.size = 65536*3/16;
  522.     if (_go32_dpmi_allocate_dos_memory(&dosmem)) {
  523.         printf("Unable to allocate dos memory - max size is %lu\n", dosmem.size);
  524.         dosmem.size = -1;
  525.         return(0);
  526.     }
  527.  
  528.     (unsigned long) sb_buf[0] = dosmem.rm_segment * 16;
  529.     (unsigned long) sb_buf[0] += 0x0FFFFL;
  530.     (unsigned long) sb_buf[0] &= 0xFFFF0000L;
  531.     (unsigned long) sb_buf[1] = (unsigned long) sb_buf[0] + 0x10000;
  532.     return(1);
  533. }
  534.  
  535. /* Initliaze our internal buffers and the card itself to prepare
  536.  * for sample playing.
  537.  *
  538.  * Call this once per program, not once per sample. */
  539. int sb_init()
  540. {
  541.     memset(&rm_regs,0,sizeof(_go32_dpmi_registers));
  542.                         /* undefined registers cause trouble */
  543.     rm_si.size = -1;    /* to allow safety check before free */
  544.     pm_si.size = -1;
  545.     dosmem.size = -1;
  546.  
  547.     sb_getparams();     /* Card card params and initialize card. */
  548.     sb_ioaddr = sb_initcard();
  549.     
  550.     if(sb_ioaddr)
  551.         /* Allocate buffers in conventional memory for double-buffering */
  552.         if(!sb_init_buffers())
  553.             return 0;
  554.     return(sb_ioaddr);
  555. }
  556.  
  557.  
  558. /* Remove interrupt handlers */
  559. void sb_cleanup_ints()
  560. {
  561.     /*  Remove our interrupt handlers */
  562.     sb_cleanup_rm_interrupt();
  563.     sb_cleanup_pm_interrupt();
  564. }
  565.  
  566. /* leave no traces on exiting module
  567.  * Call this at end of program or if you won't need sb functions anymore
  568.  */
  569. int sb_cleanup()
  570. {
  571.     if(dosmem.size == -1)    /* There is nothing to free */
  572.         return(1); 
  573.     if (!_go32_dpmi_free_dos_memory(&dosmem)) {
  574.         printf("Unable to free dos memory");
  575.         return(0);
  576.     }
  577.     return(1);
  578. }
  579.  
  580. int sb_read_counter(void)
  581. /* tells you how many bytes DMA play/recording have still to be done */
  582. {
  583.    outportb(SB_DMA_FF,0);
  584.    return(inportb(SB_DMA + 2 * sb_dmachan + 1) +
  585.           256*inportb(SB_DMA + 2 * sb_dmachan + 1));
  586. }
  587.  
  588. int mykbhit(void)
  589. {                      
  590.     return(*((short *)0xE000041a) != *((short *)0xE000041c));
  591. }
  592.  
  593. void kbclear(void)
  594. {
  595.     *((short *)0xE000041c) = *((short *)0xE000041a);
  596. }
  597.  
  598. int mygetch(void)
  599. {
  600.     short address;
  601.     short key;
  602.  
  603.     while(*((short *)0xE000041a) == *((short *)0xE000041c));
  604.     address = *((short *)0xE000041c)-2;
  605.     key = *((short *)(0xE0000400+address));
  606.     *((short *)0xE000041c) = address;
  607.     return(key & 0xFF);
  608. }
  609.  
  610. /*
  611.  * Convenient wrappers for the sound functions
  612.  */
  613. void SoundPlay(int Rate, char *data, unsigned long length)
  614. {
  615.     sb_voice(1);
  616.     sb_set_sample_rate(Rate);
  617.     sb_play(data, length);
  618.     sb_voice(0);
  619. }
  620.  
  621.  
  622. unsigned long SoundRec(int Rate, char *data, unsigned long length)
  623. {
  624.     sb_set_sample_rate(Rate);
  625.     return(sb_rec(data, length));
  626. }
  627.  
  628.